package com.shaoshaohuo.app.entity;

import java.util.HashMap;
import java.util.Map;


import com.shaoshaohuo.app.exception.GoodUnitConvertException;
import com.shaoshaohuo.app.exception.GoodUnitNatureNumberException;
import com.shaoshaohuo.app.exception.GoodUnitNotExistException;
import com.shaoshaohuo.app.exception.GoodUnitNullException;
import com.shaoshaohuo.app.utils.CollectionUtil;
import com.shaoshaohuo.app.utils.NumberUtil;
import com.shaoshaohuo.app.utils.StringUtil;

/**
 * 商品单位
 * @author YinSF
 * @createDate 2015-11-13
 */
public class GoodUnits {
	//默认为没有换算关系的单位分组
	private static final int GROUP_DEFAULT = 1;
	//重量分组
	public static final int GROUP_WEIGHT = 2;
	//所有唯一单位（没有换算关系的单位）
	private static final String[] allUniqueUnits =
		{"盆", "支", "袋", "粒", "棵", "扎", "株", "包", "头", "亩", "盒", "尾", "千粒", "箱", "件", "罐", "只", "平方米", "个",};
	//重量分组单位
	private static final String[] allWeightUnits = {"吨", "公斤", "斤", "克"};
	private static final RelationUnit KE =  new RelationUnit("克");
	private static final RelationUnit JIN =  new RelationUnit("斤", KE, 500);
	private static final RelationUnit GONG_JIN =  new RelationUnit("公斤", JIN, 2);
	private static final RelationUnit DUN =  new RelationUnit("吨", GONG_JIN, 1000);
	private static final RelationUnit[] allWeightUnitObjects = {DUN, GONG_JIN, JIN, KE};
	//单位分组索引字典
	private static final Map<String, Integer> unitIndex = new HashMap<String, Integer>();
	static {
		unitIndex.put("克", GROUP_WEIGHT);
		unitIndex.put("斤", GROUP_WEIGHT);
		unitIndex.put("公斤", GROUP_WEIGHT);
		unitIndex.put("吨", GROUP_WEIGHT);
		unitIndex.put("盆", GROUP_DEFAULT);
		unitIndex.put("支", GROUP_DEFAULT);
		unitIndex.put("袋", GROUP_DEFAULT);
		unitIndex.put("粒", GROUP_DEFAULT);
		unitIndex.put("棵", GROUP_DEFAULT);
		unitIndex.put("扎", GROUP_DEFAULT);
		unitIndex.put("株", GROUP_DEFAULT);
		unitIndex.put("包", GROUP_DEFAULT);
		unitIndex.put("头", GROUP_DEFAULT);
		unitIndex.put("亩", GROUP_DEFAULT);
		unitIndex.put("盒", GROUP_DEFAULT);
		unitIndex.put("尾", GROUP_DEFAULT);
		unitIndex.put("千粒", GROUP_DEFAULT);
		unitIndex.put("箱", GROUP_DEFAULT);
		unitIndex.put("件", GROUP_DEFAULT);
		unitIndex.put("罐", GROUP_DEFAULT);
		unitIndex.put("只", GROUP_DEFAULT);
		unitIndex.put("平方米", GROUP_DEFAULT);
		unitIndex.put("个", GROUP_DEFAULT);
	}
	//单位名称
	private String name;
	//是否是唯一单位
	private boolean isUnique;
	//单位分组
	private String unitGroup;
	
	
	/**
	 * 判断unitName是否关系单位
	 * @param unitName
	 * @return
	 */
	public static boolean isUniqueUnit(String unitName) {
		if (unitIndex.get(unitName) == GROUP_DEFAULT) {
			return true;
		}
		return false;
	}
	
	/**
	 * 获取所有的相关单位
	 * 建设先调用isUniqueUnit，判断是否为需要换算的单位了。再调用该方法
	 * @param unitName
	 * @return
	 */
	public static String[] getAllRelationUnits(String unitName) {
		if (StringUtil.isEmpty(unitName)) {
			throw new GoodUnitNullException();
		}
		switch(unitIndex.get(unitName)) {
		case GROUP_WEIGHT:
			return allWeightUnits;
		default:
			return new String[]{ unitName };
		}
	}
	
	/**
	 * 最重要的方法
	 * 单位数量换算
	 * @param fromUnit	源单位
	 * @param toUnit	目标单位
	 * @param fromAmount	源数量
	 * @return	目标数量
	 */
	public static double convertUnit(String fromUnit, String toUnit, double fromAmount) {
		if (StringUtil.isEmpty(fromUnit) || StringUtil.isEmpty(toUnit)) {
			throw new GoodUnitNullException();
		}
		if (fromUnit.equals(toUnit))
			return fromAmount;
		int fromGroup = unitIndex.get(fromUnit);
		int toGroup = unitIndex.get(toUnit);
		if (fromGroup != toGroup || fromGroup == GROUP_DEFAULT) {
			throw new GoodUnitConvertException(fromUnit, toUnit);
		}
		if (fromAmount < 0) {
			throw new GoodUnitNatureNumberException();
		}
		
		switch(fromGroup) {
		case GROUP_WEIGHT:
			return convertUntiWeight(fromUnit, toUnit, fromAmount);
		default:
			return 0;
		}
	}
	
	/**
	 * 换算重量单位
	 * @param fromUnit	源单位
	 * @param toUnit	目标单位
	 * @param fromAmount	源数量
	 * @return	目标数量
	 */
	private static double convertUntiWeight(String fromUnit, String toUnit,
			double fromAmount) {
		int fromIndex = CollectionUtil.search(allWeightUnits, fromUnit);
		int toIndex = CollectionUtil.search(allWeightUnits, toUnit);
		if (fromIndex < 0) {
			throw new GoodUnitNotExistException(fromUnit, GROUP_WEIGHT);
		}
		if (toIndex < 0) {
			throw new GoodUnitNotExistException(toUnit, GROUP_WEIGHT);
		}
		if (fromUnit.equals(toUnit)) {
			return fromAmount;
		}
		if (fromIndex > toIndex) {
			RelationUnit largerUnit = allWeightUnitObjects[toIndex];
			return NumberUtil.formatMoney(fromAmount / largerUnit.timesToLowerLevel(fromUnit), NumberUtil.FORMAT_MONEY_CEIL);
		} else if (fromIndex < toIndex) {
			RelationUnit largerUnit = allWeightUnitObjects[fromIndex];
			return NumberUtil.formatMoney(fromAmount * largerUnit.timesToLowerLevel(toUnit), NumberUtil.FORMAT_MONEY_CEIL);
		}
		
		return 0;
	}

	public static int indexOfUnitGroup(int groupId, String unit) {
		switch(groupId) {
		case GROUP_WEIGHT:
			return CollectionUtil.search(allWeightUnits, unit);
		default:
			return -1;
		}
	}
	
	/**
	 * 有换算关系的单位
	 * @author YinSF
	 * @createDate 2015-11-13
	 */
	private static class RelationUnit {
		private String name;
		//下级单位
		private RelationUnit lowerLevel;
		//本单位数量1，代表下一级单位数量多少
		private long lowerLevelTimes = 1;
		
		/**
		 * 只适用于最低等级单位创建对象
		 * @param name
		 */
		public RelationUnit(String name) {
			super();
			this.name = name;
		}
		/**
		 * 除最低级单位以外，使用该构造函数创建对象
		 * @param name
		 * @param lowerLevel
		 * @param lowerLevelTimes
		 */
		public RelationUnit(String name, RelationUnit lowerLevel,
				long lowerLevelTimes) {
			super();
			this.name = name;
			this.lowerLevel = lowerLevel;
			this.lowerLevelTimes = lowerLevelTimes;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public RelationUnit getLowerLevel() {
			return lowerLevel;
		}
		public void setLowerLevel(RelationUnit lowerLevel) {
			this.lowerLevel = lowerLevel;
		}
		public long getLowerLevelTimes() {
			return lowerLevelTimes;
		}
		public void setLowerLevelTimes(long lowerLevelTimes) {
			this.lowerLevelTimes = lowerLevelTimes;
		}
		
		/**
		 * 获取从该单位到低级单位lowerLevelUnitName的换算倍数
		 * @param lowerLevelUnitName
		 * @return
		 */
		public long timesToLowerLevel(String lowerLevelUnitName) {
			long val = getLowerLevelTimes();
			RelationUnit lowUnit = getLowerLevel();
			if (lowUnit == null) {
				return val;
			}
			if (!lowUnit.getName().equals(lowerLevelUnitName)) {
				val *= lowUnit.timesToLowerLevel(lowerLevelUnitName);
			}
			return val;
		}
		
	}
}
